package cn.hill4j.tool.spring.std.mvcext.crypto;

import cn.hill4j.tool.core.crypto.ReqDecEncUtils;
import cn.hill4j.tool.core.crypto.ReqSourceVo;
import cn.hill4j.tool.core.crypto.SimpleReqEncVo;
import cn.hill4j.tool.core.crypto.SimpleReqSourceVo;
import cn.hill4j.tool.spring.ext.mvc.MvcDecEncHandler;
import cn.hill4j.tool.spring.ext.mvc.context.RequestContextUtils;
import cn.hill4j.tool.spring.ext.mvc.decrypt.RequestDec;
import cn.hill4j.tool.spring.ext.mvc.encryption.ResponseEnc;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

@MvcDecEncHandler
public class DecStrategy {
    private String dataKey = genDataKey();
    private MD5 md5 = MD5.create();
    private RSA rsa = SecureUtil.rsa();
    public Function<ReqSourceVo,String> signHandler = reqSourceVo -> md5.digestHex(String.format("%s%s%s%s",reqSourceVo.findReqTime(),reqSourceVo.findReqId(),reqSourceVo.findReqSource(),reqSourceVo.findDataKey()));

    @RequestDec(decType = "decBody")
    public void decBody(@RequestBody SimpleReqEncVo reqEncVo){
        SimpleReqSourceVo sourceVo = ReqDecEncUtils.decReq(reqEncVo,() -> new SimpleReqSourceVo(),encDataKey -> rsa.decryptStr(encDataKey, KeyType.PublicKey),this::decData,signHandler);
        RequestContextUtils.resetReqBody(sourceVo.findReqData());
    }


    @ResponseEnc(encType = "encBody")
    public SimpleReqEncVo encBody(Object data){
        String dataStr = JSON.toJSONString(data);
        SimpleReqSourceVo sourceVo = new SimpleReqSourceVo();
        sourceVo.setReqSource("T1");
        sourceVo.setReqData(dataStr);
        sourceVo.setDataKey(genDataKey());
        sourceVo.setReqId(IdUtil.simpleUUID());
        sourceVo.setReqTime(System.currentTimeMillis());
        return ReqDecEncUtils.encReq(sourceVo,() -> new SimpleReqEncVo(),dataKey -> rsa.encryptBase64(dataKey, KeyType.PrivateKey),this::encData,signHandler);
    }

    private String genDataKey(){
        return RandomUtil.randomString(16);
    }
    private String encData(String dataKey,String data){
        return new AES(dataKey.getBytes()).encryptHex(data);
    }
    private String decData(String dataKey,String data){
        return new AES(dataKey.getBytes()).decryptStr(data);
    }

    @RequestDec(decType = "decReq")
    public void decReq(@RequestParam("sign") String sign, @RequestParam("reqTime") String reqTime, @RequestParam("reqId") String reqId, @RequestParam("encData")String encData){
        String sourceData = decData(dataKey, encData);
        String checkSign = md5.digestHex(String.format("%s%s%s",reqTime,reqId,sourceData));
        if (!Objects.equals(sign,checkSign)){
            throw new RuntimeException("请求参数验签异常");
        }
        Map<String,String> reqParams = JSON.parseObject(sourceData,Map.class);
        reqParams.forEach ((key,val) -> RequestContextUtils.resetReqQuery(key,val));
    }
    @ResponseEnc(encType = "encReq")
    public Map<String,String> encReq(Object sourceData){
        String strSourceData = JSON.toJSONString(sourceData);
        String reqTime = Objects.toString(System.currentTimeMillis());
        String reqId = IdUtil.simpleUUID();

        Map<String,String> encReqData = new HashMap<>();
        encReqData.put("reqId",reqId);
        encReqData.put("reqTime",reqTime);
        encReqData.put("sign",md5.digestHex(String.format("%s%s%s",reqTime,reqId,strSourceData)));
        encReqData.put("encData",encData(dataKey,strSourceData));

        return encReqData;
    }

}
